/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: Frank
 * 系统版本: 1.0.0
 * 开发人员: Frank
 * 开发时间: 2019/12/18 11:31
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.spring.arch.uaa.web.controller;

import com.google.common.collect.Lists;
import com.spring.arch.common.dto.result.ListResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.security.CurrentUserContextHolder;
import com.spring.arch.common.security.SecurityLog;
import com.spring.arch.common.security.UserPrincipal;
import com.spring.arch.common.utils.HttpUtils;
import com.spring.arch.uaa.bean.UserClientBean;
import com.spring.arch.uaa.dto.ClientDTO;
import com.spring.arch.uaa.dto.UserDTO;
import com.spring.arch.uaa.errorcode.UaaErrorCode;
import com.spring.arch.uaa.web.converter.UserClientConverter;
import com.spring.arch.uaa.web.dto.PasswordResetDTO;
import com.spring.arch.common.dto.result.ListResultDTO;
import com.spring.arch.common.dto.result.ResultDTO;
import com.spring.arch.common.exception.ExceptionHolder;
import com.spring.arch.common.security.CurrentUserContextHolder;
import com.spring.arch.common.security.SecurityLog;
import com.spring.arch.common.security.UserPrincipal;
import com.spring.arch.common.utils.HttpUtils;
import com.spring.arch.message.client.VerificationClient;
import com.spring.arch.message.dto.VerificationCheckParam;
import com.spring.arch.message.enums.BusinessType;
import com.spring.arch.uaa.bean.UserClientBean;
import com.spring.arch.uaa.dto.ClientDTO;
import com.spring.arch.uaa.dto.UserDTO;
import com.spring.arch.uaa.oauth2.service.OauthUserDetailsServiceImpl;
import com.spring.arch.uaa.service.UserService;
import com.spring.arch.uaa.web.converter.UserClientConverter;
import com.spring.arch.uaa.web.dto.PasswordResetDTO;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Set;
import java.util.UUID;

import static com.spring.arch.common.exception.GlobalErrorCode.E2000000001;
import static com.spring.arch.uaa.errorcode.UaaErrorCode.E2001005002;

/**
 * WEB端授权管理Api
 *
 * @author Frank
 * @version 1.0.0.1
 * @since 2019/12/18 11:31
 */
@Slf4j
@RestController
@RequestMapping(value = "/uaa/w/auth", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(tags = {"WEB端授权管理Api"}, produces = MediaType.APPLICATION_JSON_VALUE, protocols = "http, https")
public class AuthController {

    @Autowired
    private DefaultTokenServices tokenServices;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private UserClientConverter userClientConverter;
    @Autowired
    private OauthUserDetailsServiceImpl oauthUserDetailsService;
    @Resource
    private VerificationClient verificationClient;
    @Autowired
    private UserService userService;

    @PreAuthorize("#oauth2.hasAnyScope('web', 'mobile', 'third')")
    @GetMapping(value = "/clients")
    public ListResultDTO<ClientDTO> clients(@ApiIgnore OAuth2Authentication authentication) {
        if (authentication == null || !authentication.isAuthenticated()) {
            throw ExceptionHolder.serviceException(E2000000001);
        }
        String name = authentication.getName();
        String clientIP = HttpUtils.getClientIP(request);
        String clientId = authentication.getOAuth2Request().getClientId();
        log.info("[{}-{}-{}]认证成功，获取客户端信息！！！", clientIP, clientId, name);
        Set<ClientDTO> clients = userClientConverter.loadClients(name, clientId);
        return ListResultDTO.success(Lists.newArrayList(clients));
    }

    @PreAuthorize("#oauth2.hasAnyScope('web', 'mobile', 'third')")
    @GetMapping(value = "/principal")
    public ResultDTO<UserPrincipal> principal(@RequestParam(value = "clientId") UUID clientId) {
        // 获取用户授权信息
        OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw ExceptionHolder.serviceException(E2000000001);
        }
        String name = authentication.getName();
        String clientIP = HttpUtils.getClientIP(request);
        String scopeId = authentication.getOAuth2Request().getClientId();
        log.info("[{}-{}-{}-{}]认证成功，获取用户凭证信息！！！", clientIP, scopeId, name, clientId);
        UserPrincipal userPrincipal = oauthUserDetailsService.loadUserPrincipal(name, clientId);
        return ResultDTO.success(userPrincipal);
    }

    @PreAuthorize("#oauth2.hasAnyScope('web', 'mobile', 'third')")
    @GetMapping(value = "/me")
    public ResultDTO<UserDTO> userPrincipal(@RequestParam(value = "clientId") UUID clientId) {
        // 获取用户授权信息
        OAuth2Authentication auth2Authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
        if (auth2Authentication == null || !auth2Authentication.isAuthenticated()) {
            throw ExceptionHolder.serviceException(E2000000001);
        }
        String name = auth2Authentication.getName();
        String scopeId = auth2Authentication.getOAuth2Request().getClientId();
        String clientIP = HttpUtils.getClientIP(request);
        log.info("[{}-{}-{}-{}]认证成功，获取用户详情！！！", clientIP, scopeId, name, clientId);
        UserClientBean userClientBean = oauthUserDetailsService.loadUserClientBean(name, clientId);
        if (userClientBean == null) {
            throw ExceptionHolder.serviceException(UaaErrorCode.E2001005002);
        }
        UserDTO dto = userClientConverter.toDTO(userClientBean, scopeId);
        return ResultDTO.success(dto);
    }

    /**
     * 重置密码
     * @param passwordResetDTO 密码重置DTO
     * @param request Http请求
     */
    @PostMapping("/password/reset")
    public ResultDTO<Void> resetPassword(@Valid @RequestBody final PasswordResetDTO passwordResetDTO,
                                         final HttpServletRequest request) {
        // 校验验证码
        VerificationCheckParam param = new VerificationCheckParam();
        param.setBusinessType(BusinessType.CHANGE_PASSWORD);
        param.setCode(passwordResetDTO.getValidCode());
        param.setMobile(passwordResetDTO.getMobile());
        verificationClient.checkVerificationCode(param);
        // 重置密码
        userService.resetPassword(passwordResetDTO.getMobile(), passwordResetDTO.getPassword());
        // 获取ip
        final String clientIp = HttpUtils.getClientIP(request);
        if (SecurityLog.authentication.isInfoEnabled()) {
            SecurityLog.authentication.info("[{}] [{}]重置密码成功", clientIp, passwordResetDTO.getMobile());
        }
        return ResultDTO.success();
    }

    @PreAuthorize("#oauth2.hasAnyScope('web', 'mobile', 'third')")
    @PostMapping(value = "/logout")
    public ResultDTO logout(@ApiIgnore OAuth2Authentication authentication) {
        // 获取用户授权信息
        if (authentication == null || !authentication.isAuthenticated()) {
            throw ExceptionHolder.serviceException(E2000000001);
        }
        // 获取Token
        String tokenValue = HttpUtils.parseToken(request);
        // 清除Token
        boolean logoutRes = tokenServices.revokeToken(tokenValue);
        if (logoutRes) {
            // 清楚用户上下文
            CurrentUserContextHolder.clear();
            String name = authentication.getName();
            String scopeId = authentication.getOAuth2Request().getClientId();
            String clientIP = HttpUtils.getClientIP(request);
            log.info("[{}-{}-{}-{}]用户登出成功！！！", clientIP, scopeId, name);
        }
        return ResultDTO.success();
    }
}
